/*
* $Id: ComReader.cpp 347 2012-02-29 19:58:23Z volok-aleksej@yandex.ru $
*
* Copyright 2009-2011 by Volokitin Aleksey
* All rights reserved
*
*/
#include "Common/include.h"
#include <WinSock2.h>
#include "ComReader.h"
#include "Database/Database.h"
#include "Database/DatabaseNames.h"
#include "Database/Tables/TypesTable.h"
#include "Database/Tables/PointsTable.h"
#include "Database/Tables/JournalTable.h"
#include "Database/Tables/InspectTable.h"
#include "Database/Tables/FamiliesTable.h"
#include "Database/Tables/HousingsTable.h"
#include "Database/Tables/QueenBeeTable.h"
#include "Database/Tables/UserFieldsTable.h"
#include "Database/Tables/FieldValuesTable.h"
#include "Database/TransactManager.h"
#include "Database/Operation/AddPointOperation.h"
#include "Database/Operation/AddFamilyOperation.h"
#include "Database/Operation/AddInspectOperation.h"
#include "Database/Operation/AddHousingOperation.h"
#include "Database/Operation/AddQueenBeeOperation.h"
#include "Database/Operation/ChangePointOperation.h"
#include "Database/Operation/ChangeFamilyOperation.h"
#include "Database/Operation/ChangeInspectOperation.h"
#include "Database/Operation/ChangeHousingOperation.h"
#include "Database/Operation/ServiceDeleteOperation.h"
#include "Database/Operation/ChangeQueenBeeOperation.h"

#define POINTS_TABLE_ID			1
#define FAMILIES_TABLE_ID		2
#define JOURNAL_TABLE_ID		3
#define INSPECT_TABLE_ID		4
#define HOUSING_TABLE_ID		5
#define QUEEN_BEE_TABLE_ID		6
#define USER_FIELDS_TABLE_ID	7
#define FIELD_VALUES_TABLE_ID	8
#define TYPE_FRAME_TABLE_ID		9
#define TYPE_HOUSING_TABLE_ID	10

CComError::CComError(const std::tstring& strFileName, const std::tstring& strFunction, int nLine, const std::tstring& msg)
: CBaseError(strFileName, strFunction, nLine, msg){}

CComReader::CComReader(INotifier* pNotifier, bool bWrite)
	: m_pNotifier(pNotifier), m_bWrite(bWrite){
		m_database = new CDatabase(JOURNAL_DATABASE_NAME);
		m_aOperations.insert(std::make_pair(1, new CAddPointOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(2, new CAddFamilyOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(3, new CAddInspectOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(4, new CAddQueenBeeOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(5, new CAddHousingOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(6, new CChangePointOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(7, new CChangeFamilyOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(8, new CChangeInspectOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(9, new CChangeQueenBeeOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(10, new CChangeHousingOperation(&m_manager)));
		m_aOperations.insert(std::make_pair(11, new CServiceDeleteOperation(&m_manager,
													CDeleteOperation::content::POINT)));
		m_aOperations.insert(std::make_pair(12, new CServiceDeleteOperation(&m_manager,
													CDeleteOperation::content::FAMILY)));
		m_aOperations.insert(std::make_pair(13, new CServiceDeleteOperation(&m_manager,
													CDeleteOperation::content::JOURNAL)));
		m_aOperations.insert(std::make_pair(14, new CServiceDeleteOperation(&m_manager,
													CDeleteOperation::content::QUEEN_BEE)));
		m_aOperations.insert(std::make_pair(15, new CServiceDeleteOperation(&m_manager,
													CDeleteOperation::content::HOUSING)));
}
CComReader::~CComReader(){
	for(std::map<int, IServOperation*>::iterator it = m_aOperations.begin();
		it != m_aOperations.end(); it++)
		it->second->Free();
	m_database->Free();
}

int CComReader::ThreadFunc()
{
	m_hFile = CreateFile(m_strComFileName.c_str(), GENERIC_ALL, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if(m_hFile == INVALID_HANDLE_VALUE)
	{
		m_pNotifier->SendNotify(_T("Error open com port."));
		return 0;
	}
	Sleep(10);
	try{
		m_database->Open();
		if(!m_bWrite){
			char val[5];
			Read(val, sizeof(char)*5);
			if(memcmp(val, "1.0.0", 5) != 0)
			{
				m_pNotifier->SendNotify(ILanguageManager::GetLanguageManager()->GetString(SYNC_VERSION_ERROR_ID));
				return 0;
			}
			int size;
			ReadInt(size);
			m_database->BeginTransaction(_T("Synchronize"));
			for(int i = 0; i < size; i++)
				ReadData();
			m_database->EndTransaction(_T("Synchronize"));
		} else {
			std::vector<int> aIdFields, aIdInspect;
			Write("1.0.0", 5);
			for(std::map<int, std::vector<int> >::iterator it = m_aFamilies.begin();
				it != m_aFamilies.end();
				it++){
				CPointsTable points;
				points.m_content.s_nId = it->first;
				IResult* pResult = points.GetValue(m_database);
				for(int i = 0; i < (int)pResult->GetSizeResult(); i++){
					CPointsTable::content* pContent = (CPointsTable::content*)(*pResult)[i];
					WriteDBData(POINTS_TABLE_ID, pContent);
					aIdFields.push_back(pContent->s_nIdRecord);
				}
				pResult->Free();

				for(std::vector<int>::iterator it1 = it->second.begin(); it1 != it->second.end(); it1++){
					CFamiliesTable families;
					families.m_content.s_nId = *it1;
					IResult* pResult = families.GetValue(m_database);
					for(int i = 0; i < (int)pResult->GetSizeResult(); i++){
						CFamiliesTable::content* pContent = (CFamiliesTable::content*)(*pResult)[i];
						WriteDBData(FAMILIES_TABLE_ID, pContent);
						aIdFields.push_back(pContent->s_nIdRecord);
					}
					pResult->Free();

					CQueenBeeTable qb;
					qb.m_content.s_nIdFamily = *it1;
					WriteDBData(QUEEN_BEE_TABLE_ID, &qb);

					CHousingsTable housings;
					housings.m_content.s_nIdFamily = *it1;
					WriteDBData(HOUSING_TABLE_ID, &housings);

					CJournalTable journal;
					journal.m_content.s_nIdFamily = *it1;
					pResult = journal.GetValue(m_database);
					for(int i = 0; i < (int)pResult->GetSizeResult(); i++){
						CJournalTable::content* pContent = (CJournalTable::content*)(*pResult)[i];
						if(pContent->s_date > m_begin && pContent->s_date < m_end)
						{
							WriteDBData(JOURNAL_TABLE_ID, pContent);
							aIdFields.push_back(pContent->s_nIdRecord);
							aIdInspect.push_back(pContent->s_nId);
						}
					}
					pResult->Free();
				}
			}

			CTypeHousingsTable typeH;
			WriteDBData(TYPE_HOUSING_TABLE_ID, &typeH);
			CTypeFrameTable typeF;
			WriteDBData(TYPE_FRAME_TABLE_ID, &typeF);
			CUserFieldsTable userFields;
			WriteDBData(USER_FIELDS_TABLE_ID, &userFields);
			for(std::vector<int>::iterator it = aIdFields.begin(); it != aIdFields.end(); it++){
				CFieldValuesTable fieldValues;
				fieldValues.m_content.s_nId = *it;
				WriteDBData(FIELD_VALUES_TABLE_ID, &fieldValues);
			}
			for(std::vector<int>::iterator it = aIdInspect.begin(); it != aIdInspect.end(); it++){
				CInspectTable inspect;
				inspect.m_content.s_nIdJournal = *it;
				WriteDBData(INSPECT_TABLE_ID, &inspect);
			}

			WriteInt(0);
		}
		m_database->Close();
	}
	catch(CComError pError){
		m_pNotifier->SendNotify(pError.GetError());
	}
	CloseHandle(m_hFile);
	return 0;
}

void CComReader::Stop(){
	TerminateThread(m_handle, 0);
	CloseHandle(m_hFile);
	m_handle = NULL;
	m_nId = 0;
}
void CComReader::Resume(){}
void CComReader::Pause(){}

void CComReader::SetComFileName(const std::tstring& strFileName){
	m_strComFileName = strFileName;
}

void CComReader::GetBTComPath(){
	m_strComFileName.clear();
	HKEY key;
	RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Widcomm"), &key);
	if(key){
		GetWidcommComPath();
		return;
	}
}
void CComReader::GetWidcommComPath(){
	HKEY hKey;
	RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Widcomm\\BTConfig\\Services"), &hKey);
	if(!hKey)
		return;
	DWORD res = ERROR_SUCCESS;
	for(int i = 0; ; i++){
		TCHAR name[MAX_PATH] = {0};
		res = RegEnumKey(hKey, i, name, sizeof(name));
		if(res != ERROR_SUCCESS)
			break;
		HKEY subKey = 0;
		RegOpenKey(hKey, name, &subKey);
		DWORD type = REG_DWORD;
		TCHAR nValue1[4] = {0};
		DWORD nValue = 0;
		DWORD len = sizeof(nValue) + 1;
		RegQueryValueEx(subKey, _T("UUID"), 0, &type, (LPBYTE)&nValue, &len);
		if(nValue != 0x1101)
			continue;
		nValue = 0;
		RegQueryValueEx(subKey, _T("ComPortNumber"), 0, &type, (LPBYTE)&nValue, &len);
		if(nValue != 0){
			TCHAR strComNumber[10] = {0};
			_itot_s(nValue, strComNumber, 10, 10);
			m_strComFileName = m_strComFileName + _T("\\\\.\\COM") + strComNumber;
			return;
		}
	}
}

int CComReader::ReadUTF(std::tstring& value){
	char strCountSimb[2] = {0};
	Read((char*)&strCountSimb, sizeof(strCountSimb));
	short countSimb = ((short)strCountSimb[0])<<8 | strCountSimb[1];
	std::string str(countSimb, 0);
	Read((char*)str.c_str(), countSimb);
#if	defined(_UNICODE)
	int new_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), countSimb, 0, 0);
	value.resize(new_size);
	MultiByteToWideChar(CP_UTF8, 0, str.c_str(), countSimb, (LPWSTR)value.c_str(), new_size);
#else
	value = str;
#endif
	return 2 + countSimb;
}
int CComReader::ReadInt(__int32& value){
	unsigned char strCountSimb[4] = {0};
	Read((char*)&strCountSimb, sizeof(strCountSimb));
	value = ((__int32)strCountSimb[0])<<24 |
				((__int32)strCountSimb[1])<<16 |
				((__int32)strCountSimb[2])<<8 |
				strCountSimb[3];
	return 4;
}
int CComReader::ReadBool(bool& value){
	Read((char*)&value, 1);
	return 1;
}
int CComReader::ReadTime(__int64& value){
	unsigned char strCountSimb[8] = {0};
	Read((char*)&strCountSimb, sizeof(strCountSimb));
	value = (((__int64)strCountSimb[0])<<56 |
		((__int64)strCountSimb[1])<<48 |
		((__int64)strCountSimb[2])<<40 |
		((__int64)strCountSimb[3])<<32 |
		((__int64)strCountSimb[4])<<24 |
		((__int64)strCountSimb[5])<<16 |
		((__int64)strCountSimb[6])<<8 |
		strCountSimb[7])/1000;
	return 8;
}

void CComReader::WriteUTF(const std::tstring& value){
	char size[2] = {0};
	std::string utfValue;
	int new_size;
#if	defined(_UNICODE)
	new_size = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.size(), 0, 0, 0, 0);
	utfValue.resize(new_size);
	WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.size(), (LPSTR)utfValue.c_str(), new_size, 0, 0);
#else
	utfValue = value;
	new_size = value.size();
#endif
	size[0] = new_size>>8;
	size[1] = new_size;
	Write(size, 2);
	Write((char*)utfValue.c_str(), new_size);
}
void CComReader::WriteInt(__int32 value){
	char data[4] = {0};
	data[0] = value>>24;
	data[1] = value>>16;
	data[2] = value>>8;
	data[3] = value;
	Write(data, 4);
}
void CComReader::WriteBool(bool value){
	Write((char*)&value, 1);
}
void CComReader::WriteTime(__int64 value){
	char data[8] = {0};
	value = value*1000;
	data[0] = (char)(value>>56);
	data[1] = (char)(value>>48);
	data[2] = (char)(value>>40);
	data[3] = (char)(value>>32);
	data[4] = (char)(value>>24);
	data[5] = (char)(value>>16);
	data[6] = (char)(value>>8);
	data[7] = (char)value;
	Write(data, 8);
}

void CComReader::SetWriteFamilies(const std::map<int, std::vector<int> >& aFamilies)
{
	m_aFamilies = aFamilies;
}

void CComReader::SetInterval(time_t begin, time_t end)
{
	m_begin = begin;
	m_end = end;
}

void CComReader::Read(char* data, int len)
{
	DWORD reading = 0;
	do 
	{
		DWORD lenRead = 0;
		ReadFile(m_hFile, data + reading, len - reading, &lenRead, NULL);
		reading += lenRead;
	}while (len != reading);
}
void CComReader::Write(char* data, int len){
	if(!len)
		return;
	DWORD writing = 0;
	do 
	{
		DWORD lenWrite = 0;
		WriteFile(m_hFile, data + writing, len - writing, &lenWrite, NULL);
		writing += lenWrite;
		if(!lenWrite)
			Sleep(1000);
	}while (len != writing);
}

void CComReader::ReadData(){
	int type = 0;
	ReadInt(type);
	std::tstring strLog = m_aOperations[type]->ExecuteOperation(m_database, this);
	m_pNotifier->SendNotify(strLog);
}

void CComReader::WriteDBData(int id, ITable::i_content* pContent){
	WriteInt(id);
	std::tstring strLog = pContent->WriteData(this);
	if(!strLog.empty())
		m_pNotifier->SendNotify(strLog);
}

void CComReader::WriteDBData(int id, ITable* pTable){
	IResult* pResult = pTable->GetValue(m_database);
	for(int i = 0; i < (int)pResult->GetSizeResult(); i++){
		ITable::i_content* pContent = (ITable::i_content*)(*pResult)[i];
		WriteDBData(id, pContent);
	}
	pResult->Free();
}
